﻿using System;
using System.IO;
using System.Text;
using System.Threading;
using Ionic.Zip;
using Seagull.Generic;

namespace Seagull.DAL.Compress
{
    // TODO: Support cancel
    public class Compresser
    {
        public const string DefaultCompressedFileExtension = ".zip";

        private readonly string[] _filesToCompress;
        private readonly string _compressedFileExtension;
        public string SourceDir { get; private set; }

        public Compresser(string sourceDir, string compressedFileExtension = DefaultCompressedFileExtension)
        {
            SourceDir = sourceDir;
            _filesToCompress = ListAllFilesToCompress();
            _compressedFileExtension = compressedFileExtension ?? DefaultCompressedFileExtension;
        }

        public int FileCount { get { return _filesToCompress.Length; } }

        /// <summary>
        /// 压缩原目录下所有文件到目标目录，并保持目录结构。
        /// </summary>
        public void Compress(string destinationDir, CancellationToken? cancellationToken = null)
        {
            destinationDir = GenericHelper.NormalizeFolderPath(destinationDir);

            BuildStructure(destinationDir);

            // Compress all the files
            var compressedFileCount = 0;
            foreach(var filePath in _filesToCompress)
            {
                if (cancellationToken.HasValue)
                {
                    cancellationToken.Value.ThrowIfCancellationRequested();
                }
                CompressFile(filePath, filePath.Replace(SourceDir, destinationDir) + _compressedFileExtension);
                OnFileCompressed(++compressedFileCount);
            }
        }

        public event EventHandler<EventArgs<int>> FileCompressed;

        public void OnFileCompressed(int compressedFileCount)
        {
            EventHandler<EventArgs<int>> handler = FileCompressed;
            if(handler != null)
                handler(this, new EventArgs<int>(compressedFileCount));
        }

        private string[] ListAllFilesToCompress()
        {
            return Directory.GetFiles(SourceDir, "*.*", SearchOption.AllDirectories);
        }

        private void BuildStructure(string destinationDir)
        {
            // Build dir structure
            foreach(var dirPath in Directory.EnumerateDirectories(SourceDir, "*", SearchOption.AllDirectories))
            {
                Directory.CreateDirectory(dirPath.Replace(SourceDir, destinationDir));
            }
        }

        private static void CompressFile(string sourceFile, string destinationFile)
        {
            using(var zipFile = new ZipFile(destinationFile, Encoding.Default))
            {
                zipFile.AddFile(sourceFile, "\\");
                zipFile.Save();
            }
        }
    }
}
